package crp.core.course.domain.services;

import com.qwlabs.lang.C2;
import com.qwlabs.panache.Ranged;
import crp.core.course.domain.models.CourseAttributesEntity;
import crp.core.course.domain.models.CourseEnrollmentEntity;
import crp.core.course.domain.models.CourseEntity;
import crp.core.course.domain.models.CourseFacultyEntity;
import crp.core.course.domain.models.CourseFacultyRole;
import crp.core.course.domain.models.CourseSourceType;
import crp.core.course.domain.models.ExistedCourseSource;
import crp.core.course.domain.repositories.CourseAttributesRepository;
import crp.core.course.domain.repositories.CourseEnrollmentRepository;
import crp.core.course.domain.repositories.CourseFacultyRepository;
import crp.core.course.domain.repositories.CourseRepository;
import crp.core.security.Callers;
import crp.core.shared.PublicStatus;
import io.quarkus.panache.common.Range;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@ApplicationScoped
public class CourseSearcher {
    private final CourseRepository courseRepository;
    private final CourseAttributesRepository attributesRepository;
    private final CourseEnrollmentRepository enrollmentRepository;
    private final CourseFacultyRepository courseFacultyRepository;

    @Inject
    public CourseSearcher(CourseRepository courseRepository,
                          CourseAttributesRepository attributesRepository,
                          CourseEnrollmentRepository enrollmentRepository,
                          CourseFacultyRepository courseFacultyRepository) {
        this.courseRepository = courseRepository;
        this.attributesRepository = attributesRepository;
        this.enrollmentRepository = enrollmentRepository;
        this.courseFacultyRepository = courseFacultyRepository;
    }

    public Ranged<CourseEntity> by(String keyword, String academicTermId,
                                   String openDepartmentId,
                                   CourseSourceType sourceType, ExistedCourseSource existedCourseSource,
                                   Range range) {
        return Ranged.of(courseRepository.findBy(keyword, academicTermId, openDepartmentId, sourceType, existedCourseSource), range);
    }

    public Ranged<CourseEntity> publicBy(String keyword, Range range) {
        return Ranged.of(courseRepository.findBy(keyword, PublicStatus.PUBLIC), range);
    }

    public Optional<CourseEntity> byId(String id) {
        return courseRepository.findByIdOptional(id);
    }

    public Stream<CourseEntity> publicByIds(Set<String> ids) {
        return courseRepository.findByIds(ids, PublicStatus.PUBLIC);
    }

    public Stream<CourseEntity> publicByLatest(Integer limit) {
        return courseRepository.findByLatest(limit, PublicStatus.PUBLIC);
    }

    public Stream<CourseAttributesEntity> attributesByIds(Set<String> ids) {
        return attributesRepository.findByCourseIds(ids);
    }

    public Ranged<CourseEntity> studentBy(String s, String academicTermId, Range range) {
        Set<CourseEnrollmentEntity> enrollments = enrollmentRepository.findByStudentId(Callers.studentId())
                .collect(Collectors.toSet());
        Set<String> courseIds = C2.set(enrollments, item -> item.getCourse().getId());
        return Ranged.of(courseRepository.findByIds(courseIds, academicTermId), range);
    }

    public Ranged<CourseEntity> facultyBy(String academicTermId, Range range) {
        return Ranged.of(courseRepository.findByFacultyId(Callers.facultyId(), academicTermId), range);
    }

    public Stream<CourseFacultyEntity> facultiesByIds(Set<String> courseIds,
                                                      Set<CourseFacultyRole> roles) {
        return courseFacultyRepository.findByCourseIds(courseIds, roles);
    }

    public Ranged<CourseEntity> studentPublicBy(String studentId, String keyword, Range range) {
        Set<CourseEnrollmentEntity> enrollments = enrollmentRepository.findByStudentId(studentId)
                .collect(Collectors.toSet());
        Set<String> courseIds = C2.set(enrollments, item -> item.getCourse().getId());
        return Ranged.of(courseRepository.findByIdsAndKeyword(courseIds, keyword, PublicStatus.PUBLIC), range);
    }

    public Ranged<CourseEntity> facultyPublicBy(String keyword, Range range) {
        return Ranged.of(courseRepository.findByFacultyIdAndKeyword(Callers.facultyId(), keyword, PublicStatus.PUBLIC), range);
    }

    public Optional<CourseEntity> findById(String id) {
        return courseRepository.findByIdOptional(id);
    }
}
